/*
 * Copyright 2009 Cedric Priscal
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package com.zhiwei.cppapp.serialport;

import android.util.Log;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Java 串口对象操作类，用于打开串口和关闭串口
 */
public class SerialPort {
    //Tag标签
    private static final String TAG = "SerialPort";

    //加载类库，如此才能调用到C/C++里面的方法，或者是.so文件里面的方法。
    static {
        System.loadLibrary("serial-port");
    }

    //不要删除或者重命名下面的变量，因为这些都在close()对应的C/C++函数里面调用了。
    private FileDescriptor mFd;//文件描述对象，也就是串口之类的设备类的对象实例
    private FileInputStream mFileInputStream;//文件输入流
    private FileOutputStream mFileOutputStream;//文件输出流

    /**
     * 构造函数，初始化参数，串口号，波特率等
     *
     * @param device   设备文件，也就是不同编号的串口
     * @param baudrate 波特率，也就是不同的文件传输速率，或者理解为不同的数据分包大小
     * @param flags    标识位
     * @throws SecurityException 安全异常
     * @throws IOException       IO异常
     */
    public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

        //检查是否拥有串口读写权限
        if (!device.canRead() || !device.canWrite()) {
            try {
                //若没有串口读写权限，尝试修改权限，获得读写
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                //执行linux的shell命令，修改文件权限
                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
                        + "exit\n";
                su.getOutputStream().write(cmd.getBytes());
                //若是修改权限失败，抛出异常
                if ((su.waitFor() != 0) || !device.canRead()
                        || !device.canWrite()) {
                    throw new SecurityException();
                }
            } catch (Exception e) {
                //若是执行linux命令异常，对外抛出。
                e.printStackTrace();
                throw new SecurityException();
            }
        }
        //调用下面的native方法，open打开串口
        mFd = open(device.getAbsolutePath(), baudrate, flags);
        if (mFd == null) {
            //打开串口设备出现异常而失败
            Log.e(TAG, "native open returns null");
            throw new IOException();
        }
        //打开串口成功，开始获取串口的输入输出流
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
    }

    /**
     * 对外公开的输入流的获取方式，
     *
     * @return 返回的是从本地串口获取的输入流对象
     */
    public InputStream getInputStream() {
        return mFileInputStream;
    }

    /**
     * 对外公开的输出流的获取方式
     *
     * @return 返回从本地串口获取的输出流对象
     */
    public OutputStream getOutputStream() {
        return mFileOutputStream;
    }


    //静态的native本地方法，用于打开串口，调用C/C++方法，此处之所以static静态化，因为该方法在构造函数内调用了。
    private native static FileDescriptor open(String path, int baudrate, int flags);

    // JNI,调用C/C++的方法
    public native void close();
}